use openssl::symm::{decrypt_aead, encrypt_aead, Cipher};

pub const NONCE_LEN: usize = 96 / 8;
pub fn aes_128_encrypt(m_key: &[u8], nonce: &[u8], to_encrypt: String) -> Option<String> {
    let mut aes_key = [0u8; 16];
    let size = m_key.len();
    if size > 16 {
        aes_key.copy_from_slice(&m_key[..16]);
    } else {
        let mut p_m_key = m_key.iter().take(size).collect::<Vec<&u8>>();
        let mut p_m_key_vec = vec![];
        p_m_key.extend_from_slice(aes_key.iter().take(16 - size).collect::<Vec<_>>().as_ref());
        for x in &p_m_key {
            p_m_key_vec.push(**x);
        }
        aes_key.copy_from_slice(&p_m_key_vec[..16]);
    }
    let mut aes_nonce = [0u8; NONCE_LEN];

    let size = nonce.len();
    if size > NONCE_LEN {
        aes_nonce.copy_from_slice(&nonce[..NONCE_LEN]);
    } else {
        let mut p_m_nonce = nonce.iter().take(size).collect::<Vec<&u8>>();
        let mut p_m_nonce_vec = vec![];
        p_m_nonce.extend_from_slice(
            aes_nonce
                .iter()
                .take(NONCE_LEN - size)
                .collect::<Vec<_>>()
                .as_ref(),
        );
        for x in &p_m_nonce {
            p_m_nonce_vec.push(**x);
        }
        aes_nonce.copy_from_slice(&p_m_nonce_vec[..NONCE_LEN]);
    }

    let cipher = Cipher::aes_128_gcm();
    let data = to_encrypt.as_bytes();
    let aad = [0u8; 0];
    let mut actual_tag = [0u8; 16];

    match encrypt_aead(
        cipher,
        &aes_key,
        Some(&aes_nonce),
        &aad,
        data,
        &mut actual_tag,
    ) {
        Ok(encrypt_text) => {
            let mut encrypt_text_padding: Vec<u8> = [encrypt_text, actual_tag.to_vec()].concat();
            Some(hex::encode(&encrypt_text_padding))
        }
        Err(err) => {
            log::error!("aes_128_encrypt err:{}", err);
            None
        }
    }
}

pub fn aes_128_decrypt(m_key: &[u8], nonce: &[u8], to_decrypt: String) -> Option<String> {
    let mut aes_key = [0u8; 16];
    let size = m_key.len();
    if size > 16 {
        aes_key.copy_from_slice(&m_key[..16]);
    } else {
        let mut p_m_key = m_key.iter().take(size).collect::<Vec<&u8>>();
        let mut p_m_key_vec = vec![];
        p_m_key.extend_from_slice(aes_key.iter().take(16 - size).collect::<Vec<_>>().as_ref());
        for x in &p_m_key {
            p_m_key_vec.push(**x);
        }
        aes_key.copy_from_slice(&p_m_key_vec[..16]);
    }
    let mut aes_nonce = [0u8; NONCE_LEN];

    let size = nonce.len();
    if size > NONCE_LEN {
        aes_nonce.copy_from_slice(&nonce[..NONCE_LEN]);
    } else {
        let mut p_m_nonce = nonce.iter().take(size).collect::<Vec<&u8>>();
        let mut p_m_nonce_vec = vec![];
        p_m_nonce.extend_from_slice(
            aes_nonce
                .iter()
                .take(NONCE_LEN - size)
                .collect::<Vec<_>>()
                .as_ref(),
        );
        for x in &p_m_nonce {
            p_m_nonce_vec.push(**x);
        }
        aes_nonce.copy_from_slice(&p_m_nonce_vec[..NONCE_LEN]);
    }

    if let Ok(hex_byte) = hex::decode(to_decrypt.as_bytes()) {
        let hex_byte_len = hex_byte.len();
        if hex_byte_len < 16 {
            log::error!("aes gcm 128 aead tag len error!");
            return None;
        }
        let cipher = Cipher::aes_128_gcm();
        let aad = [0u8; 0];
        let mut aes_tag = [0u8; 16];
        // let mut aes_data = hex_byte.iter().take(hex_byte_len-16).collect::<Vec<&u8>>();
        let mut aes_data_decrypt = vec![];
        aes_data_decrypt.resize(hex_byte_len - 16, 0);
        aes_data_decrypt.copy_from_slice(&hex_byte[0..hex_byte_len - 16]);
        aes_tag.copy_from_slice(&hex_byte[hex_byte_len - 16..hex_byte_len]);
        match decrypt_aead(
            cipher,
            &aes_key,
            Some(&aes_nonce),
            &aad,
            &aes_data_decrypt,
            &aes_tag,
        ) {
            Ok(decrypt_text) => {
                return String::from_utf8(decrypt_text).ok();
            }
            Err(err) => {
                log::error!("aes_128_encrypt err:{}", err);
                return None;
            }
        }
    };

    None
}

#[cfg(test)]
mod tests {
    use super::{aes_128_decrypt, aes_128_encrypt};
    #[test]
    fn test_openssl_aes_gcm_128() {
        use crate::utils::logutil;
        use crate::utils::random_byte;

        logutil::global_log_init("coolink", "DEBUG");

        // let secret = b"1234567890123456";
        // let nonce = b"123456789012";
        // let secret = b"123";
        // let nonce = b"456";

        // let secret = b"123456789012345678";
        // let nonce = b"12345678901212";
        // let message = b"So many books, so little time";

        // let ciphertext = Encryptor::aes_128_gcm(secret, nonce)
        //     .unwrap()
        //     .encrypt(message.to_vec())
        //     .unwrap();

        // log::info!("Encrypt ciphertext = {:02x?} ", ciphertext);
        // assert!(ciphertext != message);

        // let plaintext = Decryptor::aes_128_gcm(secret, nonce)
        //     .unwrap()
        //     .decrypt(ciphertext)
        //     .unwrap();

        // log::info!(
        //     "Decryptor plaintext = {:?}",
        //     String::from_utf8(plaintext.clone()).unwrap()
        // );

        // assert_eq!(plaintext, message);

        let e1 = aes_128_encrypt(
            "123456".to_owned().as_bytes(),
            "123456".to_owned().as_bytes(),
            "hello,world!".to_owned(),
        );
        assert_eq!(e1.is_none(), false);
        log::info!("e1 unwrap  = {:?}", e1.clone().unwrap());

        let e2 = aes_128_decrypt(
            "123456".to_owned().as_bytes(),
            "123456".to_owned().as_bytes(),
            e1.clone().unwrap_or("".to_owned()),
        );
        assert_eq!(e2.is_none(), false);
        assert_eq!("hello,world!", e2.unwrap());

        let (app_secret, nonce) = random_byte::gen_app_secret_and_nonce("".to_owned());
        let secret_bytes = hex::decode(&app_secret).unwrap();
        let nonce_bytes = hex::decode(&nonce).unwrap();
        let some_unique_srv_id = aes_128_encrypt(
            &secret_bytes,
            &nonce_bytes,
            "group_addr_struct.unique_server_id".to_owned(),
        );
        log::info!(
            "some_unique_srv_id aes_128_encrypt  = {:?}",
            some_unique_srv_id
        );
        let dencrypt = aes_128_decrypt(&secret_bytes, &nonce_bytes, some_unique_srv_id.unwrap());
        assert_eq!("group_addr_struct.unique_server_id", dencrypt.unwrap());
    }
}
